package backup;

import entity.*;
import java.io.*;
import java.net.URL;
import java.util.*;

import crypto.DESFileManager;

import utility.FileMD5;
import utility.VDiskAPIUtility;

public class SyncMachine {
	private LocalStorage localStore;
	private CloudStorage cloudStore;
	
	public SyncMachine() {
		localStore = null;
		cloudStore = null;
	}
	
	public SyncMachine(String localpath, String clouduser, String cloudpass) {
		setLocal(localpath);
		setCloud(clouduser, cloudpass);
	}
	
	public void sync() {
		if (localStore == null || cloudStore == null)
			return;
		sync(localStore.root(), cloudStore.root());
	}
	
	public void setLocal(String localpath) {
		localStore = new LocalStorage(localpath);		
	}
	
	public void setCloud(String clouduser, String cloudpass ) {
		cloudStore = new CloudStorage(clouduser, cloudpass);		
	}
	
	
	private void sync(String localdir, int clouddir) {
		System.out.println(String.format("Sync local %s", localdir));
		File[] localfiles = localStore.list(localdir);
		List<MetaData> cloudfiles = cloudStore.list(clouddir);
		int localPtr = 0, cloudPtr = 0;
		while (localPtr < localfiles.length && 
				cloudPtr < cloudfiles.size()) {			
			File localfile = localfiles[localPtr];
			MetaData cloudfile = cloudfiles.get(cloudPtr);
			String localname = localfile.getName();
			String cloudname = cloudfile.getDecryptedName();
			System.out.println(String.format("local[%d]: %s\tcloud[%d]: %s", localPtr,localname,cloudPtr,cloudname));
			if (diffName(localname,cloudname) < 0 ) {
				//upload local to cloud
				if (localfile.isDirectory()){
					System.out.println("Upload local dir: "+localname);
					int updir = cloudStore.uploadDir(clouddir, localname);
					if (updir > 0 )
						sync(localfile.getPath(),updir);
				} else {
					System.out.println("Upload local file: "+localname);
					cloudStore.uploadFile(clouddir, localfile);
				}
				localPtr++; 
				
			}else if (diffName(localname,cloudname) > 0) {
				//download cloud to local
				if (cloudfile instanceof DirMetaData) {
					System.out.println("Download cloud dir:\t"+cloudname);
					File fileHandler = localStore.downloadDir(localdir, cloudname);
					sync(fileHandler.getPath(), Integer.parseInt(cloudfile.getId()));
				} else {
					URL downloadURL = cloudStore.getFileURL((FileMetaData)cloudfile);
					localStore.downloadFile(localdir+"\\"+cloudname, downloadURL);
					System.out.println("Download cloud file:\t"+cloudname);
				}
				cloudPtr++;				
			} else {
				// local equals to cloud by name
				//TODO: DIFF FILE
				localPtr++;
				cloudPtr++;
				System.out.println("Do not need to sync: "+ localname);
				if (localfile.isDirectory() &&
						cloudfile instanceof DirMetaData) {
					sync(localfile.getPath(), Integer.parseInt(cloudfile.getId()));
				}else if (localfile.isFile() && 
						cloudfile instanceof FileMetaData) {		
					try {
						int res = diffFile(localfile, (FileMetaData)cloudfile);
						if (res < 0) {
							// download to local
							URL downloadURL = cloudStore.getFileURL((FileMetaData)cloudfile);							
							localStore.downloadFile(localfile.getPath(), downloadURL);
							System.out.println("Download cloud file:\t"+cloudname);							
						}else if (res > 0) {
							// upload to cloud
							System.out.println("Upload local file: "+localname);
//							cloudStore.uploadFile(clouddir, localfile);
							cloudStore.updateFile(clouddir, localfile, (FileMetaData) cloudfile);
						}
					} catch (IOException e) {						
						e.printStackTrace();
					}
				}
			}			
		}
		while (localPtr < localfiles.length) {
			File localfile = localfiles[localPtr];
			String localname = localfile.getName();
			if (localfile.isDirectory()){
				System.out.println("Upload local dir: "+localname);
				int updir = cloudStore.uploadDir(clouddir, localname);
				if (updir > 0)
					sync(localfile.getPath(),updir);			
			} else {
				System.out.println("Upload local file: "+localname);
				cloudStore.uploadFile(clouddir, localfile);
			}
			localPtr++;
						
		}
		
		while (cloudPtr < cloudfiles.size()) {
			MetaData cloudfile = cloudfiles.get(cloudPtr);
			String cloudname = cloudfile.getDecryptedName();
			if (cloudfile instanceof DirMetaData) {
				System.out.println("Download cloud dir:\t"+cloudname);
				File fileHandler = localStore.downloadDir(localdir, cloudname);
				sync(fileHandler.getPath(), Integer.parseInt(cloudfile.getId()));				
			} else {
				URL downloadURL = cloudStore.getFileURL((FileMetaData)cloudfile);
				localStore.downloadFile(localdir+"\\"+cloudname, downloadURL);
				System.out.println("Download cloud file:\t"+cloudname);
			}
			cloudPtr++;						
		}
	}
	
	private int diffName(String a, String b) {
		return a.compareToIgnoreCase(b);
	}
	
	/**
	 * 
	 * @param localfile
	 * @param cloudfile
	 * @return 0 means file are the same
	 * 		   1 means localfile is newer and need upload to cloud
	 * 		  -1 means cloudfile is newwer and need download to local 
	 * @throws IOException
	 */
	private int diffFile(File localfile, FileMetaData cloudfile) throws IOException{
		String localMd5 = FileMD5.getFileMD5String(localfile);
		cloudfile = cloudStore.getFileInfo(cloudfile.getId());
		String cloudMd5 = cloudfile.getMd5();
		
		System.out.println(String.format("MD5 local:%s\nMD5 cloud:%s", localMd5,cloudMd5));
		if (localMd5.equals(cloudMd5)) {
			return 0;
		} else {
			long locallm = localfile.lastModified()/1000;
			long cloudlm = cloudfile.getLastModifedTime();
			System.out.println(String.format("LM local:%s\nLM cloud:%s", locallm,cloudlm));
			
			return locallm < cloudlm ? -1 : 1;
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		DESFileManager.loadKey();
		SyncMachine syncmachine = new SyncMachine("D:\\test", "liuyuchen@sjtu.edu.cn", "cloudbackup");
		syncmachine.sync(); 
	}

}
